// $Id: timer.c,v 1.1.1.1 2004/09/10 17:44:49 MagicalTux Exp $
// original : core.c 2003/02/26 18:03:12 Rev 1.7

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

#ifdef LCCWIN32
#include <winsock.h>
#else
#include <sys/socket.h>
#include <sys/time.h>
#endif

#include "timer.h"
#include "utils.h"

#ifdef MEMWATCH
#include "memwatch.h"
#endif

static struct TimerData *timer_data;
static int timer_data_max, timer_data_num;
static int *free_timer_list;
static int free_timer_list_max, free_timer_list_pos;

static int timer_heap_max;
static int *timer_heap = NULL;

// for debug
struct timer_func_list
{
    int  (*func) (int, unsigned int, int, int);
    struct timer_func_list *next;
    char *name;
};
static struct timer_func_list *tfl_root;

#if defined(LCCWIN32)
void gettimeofday (struct timeval *t, struct timezone *dummy)
{
    DWORD millisec = GetTickCount ();

    t->tv_sec = (int) (millisec / 1000);
    t->tv_usec = (millisec % 1000) * 1000;
}

#endif

//
int add_timer_func_list (int (*func) (int, unsigned int, int, int),
                         char *name)
{
    struct timer_func_list *tfl;

    CREATE (tfl, struct timer_func_list, 1);
    CREATE (tfl->name, char, strlen (name) + 1);

    tfl->next = tfl_root;
    tfl->func = func;
    strcpy (tfl->name, name);
    tfl_root = tfl;

    return 0;
}

char *search_timer_func_list (int (*func) (int, unsigned int, int, int))
{
    struct timer_func_list *tfl;
    for (tfl = tfl_root; tfl; tfl = tfl->next)
    {
        if (func == tfl->func)
            return tfl->name;
    }
    return "???";
}

/*----------------------------
 * 	Get tick time
 *----------------------------*/
static unsigned int gettick_cache;
static int gettick_count;
unsigned int gettick_nocache (void)
{
    struct timeval tval;
    gettimeofday (&tval, NULL);
    gettick_count = 256;
    return gettick_cache = tval.tv_sec * 1000 + tval.tv_usec / 1000;
}

unsigned int gettick (void)
{
    gettick_count--;
    if (gettick_count < 0)
        return gettick_nocache ();
    return gettick_cache;
}

/*======================================
 * 	CORE : Timer Heap
 *--------------------------------------
 */
static void push_timer_heap (int index)
{
    int  i, h;

    if (timer_heap == NULL || timer_heap[0] + 1 >= timer_heap_max)
    {
        int  first = timer_heap == NULL;

        timer_heap_max += 256;
        RECREATE (timer_heap, int, timer_heap_max);
        memset (timer_heap + (timer_heap_max - 256), 0, sizeof (int) * 256);
        if (first)
            timer_heap[0] = 0;
    }

    timer_heap[0]++;

    for (h = timer_heap[0] - 1, i = (h - 1) / 2;
         h > 0 && DIFF_TICK (timer_data[index].tick,
                             timer_data[timer_heap[i + 1]].tick) < 0;
         i = (h - 1) / 2)
    {
        timer_heap[h + 1] = timer_heap[i + 1];
        h = i;
    }
    timer_heap[h + 1] = index;
}

static int top_timer_heap ()
{
    if (timer_heap == NULL || timer_heap[0] <= 0)
        return -1;

    return timer_heap[1];
}

static int pop_timer_heap ()
{
    int  i, h, k;
    int  ret, last;

    if (timer_heap == NULL || timer_heap[0] <= 0)
        return -1;
    ret = timer_heap[1];
    last = timer_heap[timer_heap[0]];
    timer_heap[0]--;

    for (h = 0, k = 2; k < timer_heap[0]; k = k * 2 + 2)
    {
        if (DIFF_TICK
            (timer_data[timer_heap[k + 1]].tick,
             timer_data[timer_heap[k]].tick) > 0)
            k--;
        timer_heap[h + 1] = timer_heap[k + 1], h = k;
    }
    if (k == timer_heap[0])
        timer_heap[h + 1] = timer_heap[k], h = k - 1;

    for (i = (h - 1) / 2;
         h > 0
         && DIFF_TICK (timer_data[timer_heap[i + 1]].tick,
                       timer_data[last].tick) > 0; i = (h - 1) / 2)
    {
        timer_heap[h + 1] = timer_heap[i + 1], h = i;
    }
    timer_heap[h + 1] = last;

    return ret;
}

int add_timer (unsigned int tick, int (*func) (int, unsigned int, int, int),
               int id, int data)
{
    struct TimerData *td;
    int  i;

    if (free_timer_list_pos)
    {
        do
        {
            i = free_timer_list[--free_timer_list_pos];
        }
        while (i >= timer_data_num && free_timer_list_pos > 0);
    }
    else
        i = timer_data_num;
    if (i >= timer_data_num)
        for (i = timer_data_num; i < timer_data_max && timer_data[i].type;
             i++);
    if (i >= timer_data_num && i >= timer_data_max)
    {
        int  j;
        if (timer_data_max == 0)
        {
            timer_data_max = 256;
            CREATE (timer_data, struct TimerData, timer_data_max);
        }
        else
        {
            timer_data_max += 256;
            RECREATE (timer_data, struct TimerData, timer_data_max);
            if (timer_data == NULL)
            {
                printf ("out of memory : add_timer timer_data\n");
                exit (1);
            }
            memset (timer_data + (timer_data_max - 256), 0,
                    sizeof (struct TimerData) * 256);
        }
        for (j = timer_data_max - 256; j < timer_data_max; j++)
            timer_data[j].type = 0;
    }
    td = &timer_data[i];
    td->tick = tick;
    td->func = func;
    td->id = id;
    td->data = data;
    td->type = TIMER_ONCE_AUTODEL;
    td->interval = 1000;
    push_timer_heap (i);
    if (i >= timer_data_num)
        timer_data_num = i + 1;
    return i;
}

int add_timer_interval (unsigned int tick,
                        int (*func) (int, unsigned int, int, int), int id,
                        int data, int interval)
{
    int  tid;
    tid = add_timer (tick, func, id, data);
    timer_data[tid].type = TIMER_INTERVAL;
    timer_data[tid].interval = interval;
    return tid;
}

int delete_timer (int id, int (*func) (int, unsigned int, int, int))
{
    if (id <= 0 || id >= timer_data_num)
    {
        printf ("delete_timer error : no such timer %d\n", id);
        return -1;
    }
    if (timer_data[id].func != func)
    {
        printf
            ("delete_timer error : function dismatch %08x(%s) != %08x(%s)\n",
             (int) timer_data[id].func,
             search_timer_func_list (timer_data[id].func), (int) func,
             search_timer_func_list (func));
        return -2;
    }
    // そのうち消えるにまかせる
    timer_data[id].func = NULL;
    timer_data[id].type = TIMER_ONCE_AUTODEL;
    timer_data[id].tick -= 60 * 60 * 1000;
    return 0;
}

int addtick_timer (int tid, unsigned int tick)
{
    return timer_data[tid].tick += tick;
}

struct TimerData *get_timer (int tid)
{
    return &timer_data[tid];
}

int do_timer (unsigned int tick)
{
    int  i, nextmin = 1000;

#if 0
    static int disp_tick = 0;
    if (DIFF_TICK (disp_tick, tick) < -5000
        || DIFF_TICK (disp_tick, tick) > 5000)
    {
        printf ("timer %d(%d + %d)\n", timer_data_num, timer_heap[0],
                free_timer_list_pos);
        disp_tick = tick;
    }
#endif

    while ((i = top_timer_heap ()) >= 0)
    {
        if (DIFF_TICK (timer_data[i].tick, tick) > 0)
        {
            nextmin = DIFF_TICK (timer_data[i].tick, tick);
            break;
        }
        pop_timer_heap ();
        timer_data[i].type |= TIMER_REMOVE_HEAP;
        if (timer_data[i].func)
        {
            if (DIFF_TICK (timer_data[i].tick, tick) < -1000)
            {
                // 1秒以上の大幅な遅延が発生しているので、
                // timer処理タイミングを現在値とする事で
                // 呼び出し時タイミング(引数のtick)相対で処理してる
                // timer関数の次回処理タイミングを遅らせる
                timer_data[i].func (i, tick, timer_data[i].id,
                                    timer_data[i].data);
            }
            else
            {
                timer_data[i].func (i, timer_data[i].tick, timer_data[i].id,
                                    timer_data[i].data);
            }
        }
        if (timer_data[i].type & TIMER_REMOVE_HEAP)
        {
            switch (timer_data[i].type & ~TIMER_REMOVE_HEAP)
            {
                case TIMER_ONCE_AUTODEL:
                    timer_data[i].type = 0;
                    if (free_timer_list_pos >= free_timer_list_max)
                    {
                        free_timer_list_max += 256;
                        RECREATE (free_timer_list, int, free_timer_list_max);
                        memset (free_timer_list + (free_timer_list_max - 256),
                                0, 256 * sizeof (free_timer_list[0]));
                    }
                    free_timer_list[free_timer_list_pos++] = i;
                    break;
                case TIMER_INTERVAL:
                    if (DIFF_TICK (timer_data[i].tick, tick) < -1000)
                    {
                        timer_data[i].tick = tick + timer_data[i].interval;
                    }
                    else
                    {
                        timer_data[i].tick += timer_data[i].interval;
                    }
                    timer_data[i].type &= ~TIMER_REMOVE_HEAP;
                    push_timer_heap (i);
                    break;
            }
        }
    }

    if (nextmin < 10)
        nextmin = 10;
    return nextmin;
}

void timer_final ()
{
    free (timer_data);
}
